package top.eggcode.common.lang;

import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.springframework.cglib.beans.BeanCopier;
import top.eggcode.common.error.ApplicationException;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;

/**
 * Title: javaBean 处理工具
 * Description: 属性声明必须遵守”JavaBean规范“
 * Date: 2021/4/1 0:29
 *
 * @author JiaQi Ding
 * @version 1.2
 */
public final class BeanUnit {

    public static Object getAttributeValue(String name, Object model) throws InvocationTargetException, IllegalAccessException, IntrospectionException {
        PropertyDescriptor sourceProperty = new PropertyDescriptor(name, model.getClass());
        Method sourceGetter = sourceProperty.getReadMethod();
        return sourceGetter.invoke(model);
    }

    public static void setAttributeValue(String name, Object value, Object model) throws IntrospectionException, InvocationTargetException, IllegalAccessException {
        // ==// 检查字段类型和值类型是否匹配


        PropertyDescriptor targetProperty = new PropertyDescriptor(name, model.getClass());
        targetProperty.getWriteMethod().invoke(model, value);
    }

    /**
     * 转换
     *
     * @param prototype 原型
     * @param type      目标class
     * @param <T>       目标类型
     * @return 目标
     */
    public static <T> T transfer(Object prototype, Class<T> type) {
        try {
            // 创建实例
            T target = type.getDeclaredConstructor().newInstance();
            // 遍历VO对象的所有属性
            FieldUtils.getAllFieldsList(type).forEach(targetField -> {
                Field prototypeField = FieldUtils.getField(prototype.getClass(), targetField.getName(), true);
                // 属性不为空且不能被final修饰
                if (prototypeField != null && !Modifier.isFinal(prototypeField.getModifiers())) {
                    try {
                        // 获取属性值
                        Object sourceValue = getAttributeValue(targetField.getName(), prototype);
                        if (sourceValue != null) {
                            // 浅度拷贝值
                            setAttributeValue(targetField.getName(), sourceValue, target);
                        }

                    } catch (IllegalAccessException | IntrospectionException | InvocationTargetException e) {
                        throw new ApplicationException("Bean反射操作异常！", e);
                    }
                }

            });
            return target;
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
            throw new RuntimeException("Bean属性复制失败！");
        }

    }

    /**
     * 清洗字段中的特殊字符
     * [空格]
     * 如果字段值为null或者包含空格，则一律按空字符串代替
     * 注意, 本方法没有递归处理父类的属性。如有需要，请按照Java反射规范重写本方法
     *
     * @param replacer 替换者
     * @param type     实体class
     * @param <T>      实体类型
     * @return 返回一个正确的可持久化实体
     */
    public static <T> T rinseField(T replacer, Class<T> type) {
        T healthy = null;
        try {
            healthy = type.getDeclaredConstructor().newInstance();
            Field[] fieldArray = type.getDeclaredFields();
            // 排除字段
            String[] excludedFields = {"serialVersionUID"};
            for (Field field : fieldArray) {
                if (!existInArray(field.getName(), excludedFields)) {
                    String strGet = "get" +
                            field.getName().substring(0, 1).toUpperCase() +
                            field.getName().substring(1);
                    Method get = type.getMethod(strGet);
                    String val = (String) get.invoke(replacer);
                    String strSet = "set" +
                            field.getName().substring(0, 1).toUpperCase() +
                            field.getName().substring(1);
                    Method set = type.getMethod(strSet, String.class);
                    /*
                    处理特殊的值
                    清洗空格, null值
                     */
                    set.invoke(healthy,
                            StringUtils.isNotBlank(val) ? val.trim() : ""
                    );
                }

            }
        } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
            e.printStackTrace();
        }
        return healthy;
    }

    /**
     * 检查关键字是否存在字典中
     *
     * @param keyword      被查找者
     * @param dictionaries 字典
     * @return 返回结果的状态
     */
    public static boolean existInArray(String keyword, String[] dictionaries) {
        for (String item : dictionaries) {
            if (StringUtils.equals(keyword, item)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 覆盖
     * 引用类型属性是浅拷贝
     */
    public static void rewrite(Object reference, Object prototype) {
        // 遍历视图模型的所有属性
        FieldUtils.getAllFieldsList(prototype.getClass()).forEach(prototypeField -> {
            // 业务模型的属性
            Field bizField = FieldUtils.getField(reference.getClass(), prototypeField.getName(), true);

            // 业务模型中, 需要有对应字段,否则不处理
            if (bizField != null && !Modifier.isFinal(bizField.getModifiers())) {
                try {
                    // 获取属性值
                    Object attrValue = getAttributeValue(prototypeField.getName(), prototype);
                    if (attrValue != null) {
                        // 浅度拷贝值
                        setAttributeValue(prototypeField.getName(), attrValue, reference);
                    }

                } catch (IllegalAccessException | IntrospectionException | InvocationTargetException e) {
                    throw new ApplicationException("Bean反射操作异常！", e);
                }
            }

        });


    }

    /**
     * 复制属性, 注意：引用类型是浅拷贝.
     *
     * @param source 原型
     * @param target 目标
     */
    public static void copy(Object source, Object target) {
        BeanCopier copier = BeanCopier.create(source.getClass(), target.getClass(), false);
        copier.copy(source, target, null);
    }
}
